﻿using System.Linq.Expressions;
using AutoMapper;
using FlyingHat.Common.Extensions;
using FlyingHat.Core.Entities;
using FlyingHat.Model.Models.Global;
using FlyingHat.Repository.IRepository;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;

namespace FlyingHat.Repository.Repository
{
    public class BaseRepository<T> : IBaseRepository<T> where T : BaseEntity, new()
    {
        private readonly BaseContext context;
        private readonly IMapper mapper;
        public BaseRepository(BaseContext context, IMapper mapper)
        {
            this.context = context;
            this.mapper = mapper;
        }

        #region 异步

        /// <summary>
        /// 开启事务
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<IDbContextTransaction> BeginTransactionAsync(CancellationToken cancellationToken = default)
        {
            return await context.Database.BeginTransactionAsync(cancellationToken).ConfigureAwait(false);
        }
        /// <summary>
        /// 提交事务
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task CommitTransactionAsync(CancellationToken cancellationToken = default)
        {
            await context.Database.CommitTransactionAsync(cancellationToken).ConfigureAwait(false);
        }
        /// <summary>
        /// 回滚事务
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task RollbackTransactionAsync(CancellationToken cancellationToken = default)
        {
            await context.Database.RollbackTransactionAsync(cancellationToken).ConfigureAwait(false);
        }


        public async Task<long> AddReturnIdAsync(T entity, CancellationToken cancellationToken = default)
        {
            await context.Set<T>().AddAsync(entity, cancellationToken);
            var row = await context.SaveChangesAsync(cancellationToken);
            return row > 0 ? entity.Id : 0;
        }
        public async Task<int> AddAsync(T entity, CancellationToken cancellationToken = default)
        {
            await context.Set<T>().AddAsync(entity, cancellationToken).ConfigureAwait(false);
            var row = await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
            return row;
        }
        public async Task<int> AddRangeAsync(List<T> entities, CancellationToken cancellationToken = default)
        {
            await context.Set<T>().AddRangeAsync(entities, cancellationToken).ConfigureAwait(false);
            var row = await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
            return row;
        }
        public async Task<bool> UpdateAsync(T entity, CancellationToken cancellationToken = default)
        {
            await Task.Run(() => context.Set<T>().Update(entity));
            return await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false) > 0;
        }

        public async Task<bool> UpdateAsync(T entity, Expression<Func<T, object>> expression, CancellationToken cancellationToken = default)
        {
            try
            {
                await Task.Run(() =>
                {
                    context.Set<T>().Update(entity);
                    var entry = context.Entry<T>(entity);
                    entry.State = EntityState.Unchanged;
                    foreach (var proInfo in expression.GetPropertyAccessList())
                    {
                        if (!string.IsNullOrEmpty(proInfo.Name))
                        {
                            entry.Property(proInfo.Name).IsModified = true;
                        }
                    }
                }, cancellationToken);
                return await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false) > 0;
            }
            catch
            {
                return false;
            }
        }

        public async Task<bool> UpdateAsync(List<T> entities, Expression<Func<T, object>> expression, CancellationToken cancellationToken = default)
        {
            try
            {
                foreach (var item in entities)
                {
                    // 此处赋值给一个新的变量，开启一个当前任务内的内存
                    var entity = item;
                    await Task.Run(() =>
                    {
                        context.Set<T>().Update(entity);
                        var entry = context.Entry<T>(entity);
                        entry.State = EntityState.Unchanged;
                        foreach (var proInfo in expression.GetPropertyAccessList())
                        {
                            if (!string.IsNullOrEmpty(proInfo.Name))
                            {
                                entry.Property(proInfo.Name).IsModified = true;
                            }
                        }
                    }, cancellationToken);
                }
                return await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false) > 0;
            }
            catch
            {
                return false;
            }
        }
        public async Task<bool> UpdateExclude(T entity, Expression<Func<T, object>> expression, CancellationToken cancellationToken = default)
        {

            try
            {
                await Task.Run(() =>
                {
                    context.Set<T>().Update(entity);
                    var entry = context.Entry<T>(entity);
                    entry.State = EntityState.Modified;
                    foreach (var proInfo in expression.GetPropertyAccessList())
                    {
                        if (!string.IsNullOrEmpty(proInfo.Name))
                        {
                            entry.Property(proInfo.Name).IsModified = false;
                        }
                    }
                }, cancellationToken);

                return await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false) > 0;
            }
            catch
            {
                return false;
            }
        }
        public async Task<bool> DeleteAsync(Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default)
        {
            // TODO
            //此处待优化，目前我们先不引入第三方插件，等待后续讲到给项目升级的时候会依次加入第三方组件
            await Task.Run(() =>
            {
                var list = context.Set<T>().Where(predicate).Select(i => new T { Id = i.Id }).AsEnumerable();
                context.Set<T>().RemoveRange(list);
            }, cancellationToken);
            return await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false) > 0;
        }

        public async Task<bool> DeleteAsync(long id, CancellationToken cancellationToken = default)
        {
            await Task.Run(() =>
            {
                var t = new T() { Id = id };
                context.Set<T>().Remove(t);
            }, cancellationToken);

            return await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false) > 0;
        }

        public async Task<bool> DeleteAsync(List<long> ids, CancellationToken cancellationToken = default)
        {
            await Task.Run(() =>
            {
                var list = ids.Select(i => new T { Id = i });
                context.Set<T>().RemoveRange(list);
            }, cancellationToken).ConfigureAwait(false);

            return await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false) > 0;
        }

        public async Task<IQueryable<T>> GetAllAsync(CancellationToken cancellationToken = default)
        {
            return await Task.Run(() =>
            {
                return context.Set<T>().AsQueryable();
            }, cancellationToken).ConfigureAwait(false);
        }

        public async Task<List<T>> GetListAsync(CancellationToken cancellationToken = default)
        {
            return await context.Set<T>().ToListAsync(cancellationToken).ConfigureAwait(false);
        }

        public async Task<int> CountAsync(Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default)
        {
            return await context.Set<T>().CountAsync(predicate, cancellationToken).ConfigureAwait(false);
        }

        public async Task<bool> ExistAsync(Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default)
        {
            return await context.Set<T>().AnyAsync(predicate, cancellationToken).ConfigureAwait(false);
        }
        public async Task<List<T>> GetListAsync(Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default)
        {
            return await context.Set<T>().Where(predicate).ToListAsync(cancellationToken).ConfigureAwait(false);
        }
        public async Task<IReturnModel> GetListAsync<K, M>(Expression<Func<T, bool>> predicate, Expression<Func<T, K>> keySelector, int page, int limit, bool asc = false, CancellationToken cancellationToken = default)
        {
            var res = new List<M>();
            var count = await context.Set<T>().Where(predicate).LongCountAsync(cancellationToken).ConfigureAwait(false);
            if (count == 0)
            {
                return ReturnModel.Page(count, res);
            }
            var list = await context.Set<T>().Where(predicate).OrderBy(keySelector, asc).Skip((page - 1) * limit).Take(limit).ToListAsync(cancellationToken);
            res = mapper.Map<List<M>>(list);
            return ReturnModel.Page(count, res);
        }
        public async Task<IReturnModel> GetListAsync<K>(Expression<Func<T, bool>> predicate, Expression<Func<T, K>> keySelector, int page, int limit, bool asc = false, CancellationToken cancellationToken = default)
        {
            var list = new List<T>();
            var count = await context.Set<T>().Where(predicate).LongCountAsync(cancellationToken).ConfigureAwait(false);
            if (count == 0)
            {
                return ReturnModel.Page(count, list);
            }
            list = await context.Set<T>().Where(predicate).OrderBy(keySelector, asc).Skip((page - 1) * limit).Take(limit).ToListAsync(cancellationToken);
            return ReturnModel.Page(count, list);
        }
        public async Task<T?> FirstOrDefaultAsync(long id, CancellationToken cancellationToken = default)
        {
            return await context.Set<T>().FirstOrDefaultAsync(i => i.Id == id, cancellationToken).ConfigureAwait(false);
        }

        public async Task<T?> FirstOrDefaultAsync(Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default)
        {
            return await context.Set<T>().FirstOrDefaultAsync(predicate, cancellationToken).ConfigureAwait(false);
        }

        #endregion
    }
}
